#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "config.h"

#include "esp_log.h"

#define TAG "config"
#define log_e(...) ESP_LOGE(TAG, __VA_ARGS__)
#define log_i(...) ESP_LOGI(TAG, __VA_ARGS__)
#define log_w(...) ESP_LOGW(TAG, __VA_ARGS__)

/* Linux 测试代码 */
// #define IN_LINUX

/* 配置文件使用文件系统读取和保存 */
#define CONFIG_USE_FS

#ifndef IN_LINUX

#include "esp_heap_caps.h"
#ifdef CONFIG_SPIRAM
#define _malloc_(size) heap_caps_malloc(size, MALLOC_CAP_SPIRAM)
#else
#define _malloc_(size) malloc(size)
#endif

#define log_write printf

#else
#define CONFIG_DEBUG
#define _malloc_(size) malloc(size)
#define log_write printf
#endif /* IN_LINUX */

#pragma pack(1)
typedef struct config
{
    char name[33];
    uint8_t change : 1;
    uint16_t count;
    uint8_t *end;
    uint16_t size;
    uint8_t buffer[8192];
} config_s, *config_t;
config_t global_cfg = 0;
#pragma pack()

#ifdef CONFIG_USE_FS
bool config_read(void *config)
{
    if (!config)
        return false;
    config_t cfg = config;
    FILE *fd = fopen(cfg->name, "r");
    if (!fd)
    {
#ifdef CONFIG_DEBUG
        log_write("获取文件数据: 无法打开文件\n");
#endif
        return false;
    }
    fread(&cfg->size, 1, sizeof(cfg->size), fd);
    uint16_t read_len = fread(cfg->buffer, 1, cfg->size, fd);
    fclose(fd);
#ifdef CONFIG_DEBUG
    log_write("读取大小 %d 实际大小 %d\n", read_len, cfg->size + 2);
#endif
    return cfg->size == read_len;
}
/* 保存配置 */
bool config_write(void *config)
{
    if (!config)
        return false;
    config_t cfg = config;
#ifdef CONFIG_DEBUG
    log_write("%d\n", cfg->change);
#endif
    /* 无需修改 */
    if (!cfg->change)
    {
#ifdef CONFIG_DEBUG
        log_write("数据没有改动, 无需保存\n");
#endif
        return true;
    }
    FILE *fd = fopen(cfg->name, "w");
    if (!fd)
    {
#ifdef CONFIG_DEBUG
        log_write("无法打开文件\n");
#endif
        return false;
    }
    uint16_t write_len = fwrite(&cfg->size, 1, cfg->size + 2, fd);
    fclose(fd);
#ifdef CONFIG_DEBUG
    log_write("写入大小 %d 实际大小 %d\n", write_len, cfg->size + 2);
#endif
    cfg->change = write_len == (cfg->size + 2);
    return write_len == (cfg->size + 2);
}
#endif

bool config_print_from(void *config, uint8_t format)
{
    if (!config)
        return false;
    config_t cfg = config;
    uint8_t *p = cfg->buffer;
    config_item_t item;
    log_write("{");
    for (; p < cfg->buffer + sizeof(cfg->buffer);)
    {
        /* 存在 */
        if (p[0])
        {
            if (p > cfg->buffer)
            {
                log_write(",");
            }
            if (format)
                log_write("\n  ");
            item = (config_item_t)p;
            if (item->len == 1)
                log_write("\"%s\":%d", item->name, *(uint8_t *)item->data);
            else if (item->len == 2)
                log_write("\"%s\":%d", item->name, *(uint16_t *)item->data);
            else if (item->len == 4)
                log_write("\"%s\":%ld", item->name, *(uint32_t *)item->data);
            else
            {
                uint8_t is_str = 1;
                for (uint16_t j = 0; j < item->len; j++)
                {
                    if (' ' > item->data[j] || item->data[j] > '~')
                    {
                        is_str = 0;
                        break;
                    }
                }
                if (is_str)
                    log_write("\"%s\":\"%s\"", item->name, (char *)item->data);
                else
                {
                    log_write("%s:[", item->name);
                    for (uint16_t j = 0; j < item->len; j++)
                    {
                        if (j)
                        {
                            log_write(",");
                        }
                        log_write("%u", item->data[j]);
                    }
                    log_write("]");
                }
            }
            p += sizeof(config_item_s) + item->len;
        }
        else
            break;
    }
    if (format)
        log_write("\n}");
    else
        log_write("}");
    return true;
}

uint16_t config_print_from_to(void *config, void *out, uint16_t len)
{
    if (!config || !out)
        return 0;
    config_t cfg = config;
    uint8_t *p = cfg->buffer;
    uint16_t tmp_len = 0;
    char *data = out;
    config_item_t item;
    char buf[64];
    *data++ = '{';
    len -= 3;
    for (; p < cfg->buffer + sizeof(cfg->buffer);)
    {
        /* 存在 */
        if (p[0])
        {
            item = (config_item_t)p;
            if (item->len == 1 || item->len == 2 || item->len == 4)
            {
                if (item->len == 1)
                    tmp_len = snprintf(buf, sizeof(buf), "\"%s\":%u", item->name, *(uint8_t *)item->data);
                else if (item->len == 2)
                    tmp_len = snprintf(buf, sizeof(buf), "\"%s\":%u", item->name, *(uint16_t *)item->data);
                else if (item->len == 4)
                    tmp_len = snprintf(buf, sizeof(buf), "\"%s\":%lu", item->name, *(uint32_t *)item->data);
                if (p > cfg->buffer)
                {
                    if (len < tmp_len + 1)
                    {
                        log_write("Output buffer too small\n");
                        break;
                    }
                    *data++ = ',';
                    len--;
                }
                else if (len < tmp_len)
                {
                    log_write("Output buffer too small\n");
                    break;
                }
                strcpy(data, buf);
                data += tmp_len;
                len -= tmp_len;
            }
            else
            {
                uint8_t is_str = 1;
                for (uint16_t j = 0; j < item->len; j++)
                {
                    if (' ' > item->data[j] || item->data[j] > '~')
                    {
                        is_str = 0;
                        break;
                    }
                }
                if (is_str)
                {
                    if (p > cfg->buffer)
                    {
                        if (len < 5 + strnlen(item->name, 32) + strnlen(item->data, item->len) + 1)
                        {
                            log_write("Output buffer too small\n");
                            break;
                        }
                        *data++ = ',';
                        len--;
                    }
                    else if (len < 5 + strnlen(item->name, 32) + strnlen(item->data, item->len))
                    {
                        log_write("Output buffer too small\n");
                        break;
                    }
                    tmp_len = snprintf(data, len, "\"%s\":\"%s\"", item->name, (char *)item->data);
                    data += tmp_len;
                    len -= tmp_len;
                }
                else
                {
                    if (p > cfg->buffer)
                    {
                        if (len < 5 + 4 * item->len)
                        {
                            log_write("Output buffer too small\n");
                            break;
                        }
                        *data++ = ',';
                        len--;
                    }
                    else if (len < 5 + 4 * item->len - 1)
                    {
                        log_write("Output buffer too small\n");
                        break;
                    }
                    tmp_len = snprintf(buf, sizeof(buf), "\"%s\":", item->name);
                    strcpy(data, buf);
                    data += tmp_len;
                    len -= tmp_len;
                    *data++ = '[';
                    len -= 2;
                    for (uint16_t j = 0; j < item->len; j++)
                    {
                        if (j)
                        {
                            *data++ = ',';
                            len--;
                        }
                        tmp_len = snprintf(buf, sizeof(buf), "%u", item->data[j]);
                        strcpy(data, buf);
                        data += tmp_len;
                        len -= tmp_len;
                    }
                    *data++ = ']';
                }
            }
            p += sizeof(config_item_s) + item->len;
        }
        else
            break;
    }
    *data++ = '}';
    *data = '\0';
    return data - (char *)out;
}

bool config_init(char *name)
{
    if (!global_cfg)
    {
        global_cfg = _malloc_(sizeof(config_s));
        if (!global_cfg)
        {
#ifdef CONFIG_DEBUG
            log_write("无法申请内存 %d\n", sizeof(config_s));
#endif
            return false;
        }
        memset(global_cfg, 0, sizeof(config_s));
        global_cfg->end = global_cfg->buffer;
    }
    strncpy(global_cfg->name, name, sizeof(global_cfg->name) - 1);
    config_read(global_cfg);
    return true;
}

/* 获取指定配置是否存在 */
config_item_t config_find(char *name)
{
    if (!global_cfg || !global_cfg->size)
        return 0;
    uint8_t *p = global_cfg->buffer;
    config_item_t item;
    for (; p < global_cfg->buffer + sizeof(global_cfg->buffer);)
    {
        /* 存在 */
        if (p[0])
        {
            item = (config_item_t)p;
            if (!strncmp(item->name, name, sizeof(item->name)))
                return item;
            p += sizeof(config_item_s) + item->len;
        }
        else
            break;
    }
    return 0;
}

void *config_get(char *name, uint16_t *len)
{
    if (!global_cfg || !global_cfg->size)
        return 0;
    config_item_t item = config_find(name);
    if (!item)
        return 0;
    if (len)
        *len = item->len;
    return item;
}

bool config_set(char *name, uint16_t len, uint8_t *data)
{
    if (!global_cfg)
        return false;
    config_item_t item = config_find(name);
    if (item)
    {
#ifdef CONFIG_DEBUG
        log_write("查找到相同对象(%s)数据, 进行覆盖检查 : ", name);
#endif
        if (item->len == len && !memcmp(item->data, data, len))
        {
#ifdef CONFIG_DEBUG
            log_write("数据一致, 无需修改\n");
#endif
            return true;
        }
#ifdef CONFIG_DEBUG
        log_write("数据不一致, 需要修改\n");
#endif
        uint16_t offset = (void *)item - (void *)global_cfg->buffer;
        uint16_t size = sizeof(config_item_s) + item->len;
        if (global_cfg->size - offset - size)
            memmove(&global_cfg->buffer[offset], &global_cfg->buffer[offset + size], global_cfg->size - offset - size);
        global_cfg->size -= size;
    }
    item = (config_item_t)&global_cfg->buffer[global_cfg->size];
    global_cfg->size += sizeof(config_item_s) + len;
    if (global_cfg->size > sizeof(global_cfg->buffer))
    {
        len -= global_cfg->size - sizeof(global_cfg->buffer);
        global_cfg->size = sizeof(global_cfg->buffer);
#ifdef CONFIG_DEBUG
        log_write("-------------  数据缓冲区溢出  -------------\n");
#endif
    }
    strncpy(item->name, name, sizeof(item->name));
    item->len = len;
    memcpy(item->data, data, len);
    global_cfg->change = 1;
    return true;
}

bool config_print(uint8_t format)
{
    return config_print_from(global_cfg, format);
}

uint16_t config_print_to(void *out, uint16_t len)
{
    return config_print_from_to(global_cfg, out, len);
}

bool config_commit(void)
{
    if (!global_cfg)
        return false;
    return config_write(global_cfg);
}
#ifndef IN_LINUX
// static void config_auto_init(void)
// {
//     if (config_init("/storage/config.d"))
//     {
//         log_i("Configuration initialization successful");
//     }
//     else
//     {
//         log_e("Configuration initialization failed");
//     }
// }
// INIT_BOARD_EXPORT(config_auto_init);
#else
static void config_hex_print(uint8_t *data, uint16_t size)
{
    if (!data || !size)
        return;
    uint16_t i = 0;
    for (; i < size; i++)
    {
        if (i % 16 == 0)
        {
            printf("\t");
            if (i)
            {
                for (uint16_t j = 0; j < 16; j++)
                {
                    if (data[i - 16 + j] < ' ' || data[i - 16 + j] > '~')
                        printf(".");
                    else
                        printf("%c", data[i - 16 + j]);
                }
            }
            printf("\n");
        }
        printf("%02X ", data[i]);
    }
    if (i % 16)
    {
        for (uint16_t j = i % 16; j < 16; j++)
            printf("   ");
        printf("\t");
        for (uint16_t j = 0; j < i % 16; j++)
        {
            if (data[i - i % 16 + j] < ' ' || data[i - i % 16 + j] > '~')
                printf(".");
            else
                printf("%c", data[i - i % 16 + j]);
        }
    }
    printf("\n");
}

int main(int args, char **argv)
{
    printf("\n");
    if (config_init("./config_test"))
    {
        printf("初始化成功.\n");
        printf("原始数据:");
        config_hex_print(global_cfg->buffer, global_cfg->size);
        if (config_set("test_name", strlen("test_data"), "test_data"))
        {
            printf("test_name 设置成功, 更新数据:");
            config_hex_print(global_cfg->buffer, global_cfg->size);
            uint16_t len = 0;
            uint8_t *data = config_get("test_name", &len);
            if (data)
                printf("查询到数据 test_name: 长度:%d 内容: %s\n", len, (char *)data);
        }
        if (config_set("test_name2", strlen("test_data2"), "test_data2"))
        {
            printf("test_name2 设置成功, 更新数据:");
            config_hex_print(global_cfg->buffer, global_cfg->size);
            uint16_t len = 0;
            uint8_t *data = config_get("test_name2", &len);
            if (data)
                printf("查询到数据 test_name2: 长度:%d 内容: %s\n", len, (char *)data);
        }
        if (config_set("test_name3", strlen("test_data3"), "test_data3"))
        {
            printf("test_name3 设置成功, 更新数据:");
            config_hex_print(global_cfg->buffer, global_cfg->size);
            uint16_t len = 0;
            uint8_t *data = config_get("test_name3", &len);
            if (data)
                printf("查询到数据 test_name3: 长度:%d 内容: %s\n", len, (char *)data);
        }
        uint8_t n_data[8] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8};
        if (config_set("test_name4", sizeof(n_data), n_data))
        {
            printf("test_name4 设置成功, 更新数据:");
            config_hex_print(global_cfg->buffer, global_cfg->size);
            uint16_t len = 0;
            uint8_t *data = config_get("test_name4", &len);
            if (data)
                printf("查询到数据 test_name4: 长度:%d\n", len);
        }
        uint8_t u8_data = 18;
        if (config_set("test_name5", sizeof(u8_data), &u8_data))
        {
            printf("test_name5 设置成功, 更新数据:");
            config_hex_print(global_cfg->buffer, global_cfg->size);
            uint16_t len = 0;
            uint8_t *data = config_get("test_name5", &len);
            if (data)
                printf("查询到数据 test_name5: 长度:%d\n", len);
        }
        uint16_t u16_data = 1829;
        if (config_set("test_name6", sizeof(u16_data), &u16_data))
        {
            printf("test_name6 设置成功, 更新数据:");
            config_hex_print(global_cfg->buffer, global_cfg->size);
            uint16_t len = 0;
            uint8_t *data = config_get("test_name6", &len);
            if (data)
                printf("查询到数据 test_name6: 长度:%d\n", len);
        }
        uint32_t u32_data = 18294269;
        if (config_set("test_name7", sizeof(u32_data), &u32_data))
        {
            printf("test_name7 设置成功, 更新数据:");
            config_hex_print(global_cfg->buffer, global_cfg->size);
            uint16_t len = 0;
            uint8_t *data = config_get("test_name7", &len);
            if (data)
                printf("查询到数据 test_name7: 长度:%d\n", len);
        }
        {
            uint16_t len = 0;
            uint8_t *data = config_get("test_name4", &len);
            if (data)
                printf("查询到数据 test_name4: 长度:%d 内容: %s\n", len, (char *)data);
            else
                printf("没有查找到 test_name4\n");
        }
        // if (config_set("test_name", strlen("test_data new"), "test_data new"))
        // {
        //     printf("test_name 设置成功, 更新数据:");
        //     config_hex_print(global_cfg->buffer, global_cfg->size);
        //     uint16_t len = 0;
        //     uint8_t *data = config_get("test_name", &len);
        //     if (data)
        //         printf("查询到数据 test_name: 长度:%d 内容: %s\n", len, (char *)data);
        // }
        printf("\nprint       : ------------------\n");
        config_print_from(global_cfg, 0);
        printf("\nprint       : ------------------\n");
        config_print_from(global_cfg, 1);
        printf("\nprint to    : ------------------\n");
        char data[4096];
        config_print_from_to(global_cfg, data, sizeof(data));
        printf(data);
        printf("\n------------------\n");
        if (config_write(global_cfg))
        {
            printf("保存成功\n");
        }
        else
            printf("保存失败\n");
    }
    else
    {
        printf("初始化失败\n");
    }
    return 0;
}
#endif
